/*
 * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
 *
 * This file is part of the Modbus library for Beremiz and matiec.
 *
 * This Modbus library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
 *
 * This code is made available on the understanding that it will not be
 * used in safety-critical situations without a full and competent review.
 */

/* Time handling functions used by the modbus protocols... */

#ifndef __MODBUS_TIME_UTIL_H
#define __MODBUS_TIME_UTIL_H

#ifdef USE_LWIP

#include "lwip/sockets.h"

#endif

#include"Rtos.h"
#include"kSerial.h"
#include"logger_rte.h"

/* Function to load a struct timeval correctly from a double. */
static inline struct timeval d_to_timeval(double time) {
    struct timeval tmp;

    tmp.tv_sec = (time_t) time;
    tmp.tv_usec = (1e6 * (time - (double) tmp.tv_sec));
    return tmp;
}

/* Function to load a struct timespec correctly from a double. */
static inline struct timespec d_to_timespec(double time) {
    struct timespec tmp;

    tmp.tv_sec = (time_t)time;
    tmp.tv_nsec =(long) (1.0e9 * (time - (double)tmp.tv_sec));
    return tmp;
}

/* Function to ... */
static inline struct timespec timespec_dif(struct timespec *ts1, struct timespec *ts2) {
    struct timespec ts;

    ts.tv_sec = ts1->tv_sec - ts2->tv_sec;
    if (ts1->tv_nsec > ts2->tv_nsec) {
        ts.tv_nsec = ts1->tv_nsec - ts2->tv_nsec;
    } else {
        ts.tv_nsec = 1000000000 + ts1->tv_nsec - ts2->tv_nsec;
        ts.tv_sec--;
    }

    if (ts.tv_sec < 0)
        ts.tv_sec = ts.tv_nsec = 0;

    return ts;
}

/* Function to ... */
static inline struct timespec timespec_add(struct timespec *ts1, struct timespec *ts2) {
    struct timespec ts;

    ts.tv_sec = ts1->tv_sec + ts2->tv_sec;
    ts.tv_nsec = ts1->tv_nsec + ts2->tv_nsec;
    ts.tv_sec += ts.tv_nsec / 1000000000;
    ts.tv_nsec = ts.tv_nsec % 1000000000;
    return ts;
}

/* Function to convert a struct timespec to a struct timeval. */
static inline struct timeval timespec_to_timeval(struct timespec *ts) {
    struct timeval tv;

    tv.tv_sec = ts->tv_sec;
    tv.tv_usec = ts->tv_nsec / 1000;
    return tv;
}

/*
 * NOTE: clock_gettime() is rather expensive, between 7000 and 7500 clock
 *       cycles (measured with rdtsc on an Intel Pentium)
 *       gettimeofday() is half as expensive (3000 to 3500 clock cycles),
 *       but is not POSIX compliant... :-(
 *       Nevertheless this is peanuts (20 us on a 350 MHz cpu) compared to
 *       the timescales required to read a modbus frame over a serial bus
 *       (aprox. 10 ms for a 10 byte frame on a 9600 baud bus!)
 */

static inline struct timespec timespec_add_curtime(struct timespec *ts) {
    struct timespec res = {.tv_sec = 0, .tv_nsec = 0};

    /* is ts = 0 also return 0 !! */
    if ((ts->tv_sec != 0) || (ts->tv_nsec != 0)) {
         timespec_add(&res, ts);
    }
    return res;
}

#ifdef MODBUS_TCP

int my_select_tcp(int fd, fd_set *rfds, fd_set *wfds, struct timespec *end_time);

#endif
#endif /* __MODBUS_TIME_UTIL_H */
